package serveur;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import DTO.AnnonceDTO;
import DTO.ResumeAnnonceDTO;
import DTO.VendeurDTO;



/**
 * 
 * Classe permettant de faire la liaison entre le logiciel et la table "Annonces" de la BDD.
 * 
 * @author Valentin Brémond
 *
 * @version 1.0
 * 
 */
public class AnnonceDAO
{
	/**
	 * <i>Cet attribut est uniquement présent à des fins de test avec JUnit.</i>
	 * <p>
	 * Chaque méthode vérifie cet attribut : s'il est à "true", alors l'auto-commit est désactivé pour les transactions.
	 * Ceci permet de tester la classe avec JUnit sans pour autant surcharger la BDD de données de test.
	 */
	private static boolean disableAutoCommit = false;
	
	/**
	 * L'adresse de la base de données.
	 */
	private final static String adresseBDD = "jdbc:mysql://localhost/petitesannonces";
	
	/**
	 * Login pour se connecter à la BDD.
	 */
	private final static String loginBDD = "root";
	
	/**
	 * Mot de passe.
	 */
	private final static String mot_de_passeBDD = "root";
	
	/**
	 * Permet de n'instancier qu'un seul objet de cette classe.
	 */
	private static AnnonceDAO singleton;
	
	
	
	// CONSTRUCTEUR
	
	
	
	/**
	 * Constructeur par défaut.
	 */
	private AnnonceDAO()
	{
		try
		{
			Class.forName("com.mysql.jdbc.Driver");
		}
		catch (ClassNotFoundException cnfE)
		{
			System.err.println("Erreur lors du chargement du pilote.");
		}
	}
	
	
	
	// METHODES
	
	
	
	/**
	 * Permet de récupérer l'instance unique de la classe {@link AnnonceDAO}.
	 * 
	 * @return L'unique instance de la classe AnnonceDAO.
	 */
	public static AnnonceDAO getInstance()
	{
		if(AnnonceDAO.singleton == null)
		{
			singleton = new AnnonceDAO();
		}
		
		return singleton;
	}
	
	
	
	/**
	 * Permet d'ajouter une annonce (de type {@link AnnonceDAO}) dans la BDD.
	 * 
	 * @param annonce L'annonce à ajouter dans la BDD.
	 * 
	 * @return Le nombre de lignes modifiées dans la BDD.
	 */
	public int ajouterAnnonce(AnnonceDTO annonce)
	{
		Connection connection = null;
		PreparedStatement ajout = null;
		
		int retour = 0;
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			ajout = connection.prepareStatement("INSERT INTO annonces(id_vendeur, date, titre, contenu, departement, emploi_stage, objet_service) VALUES(?, ?, ?, ?, ?, ?, ?)");
			ajout.setInt(1, annonce.getId_vendeur());
			ajout.setDate(2, annonce.getDate());
			ajout.setString(3, annonce.getTitre());
			ajout.setString(4, annonce.getContenu());
			ajout.setString(5, annonce.getDepartement());
			ajout.setBoolean(6, annonce.isEmploi_stage());
			ajout.setBoolean(7, annonce.isObjet_service());
			
			retour = ajout.executeUpdate();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{	
			if(ajout != null)
			{
				try
				{
					ajout.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}
		
		return retour;
	}
	
	
	
	/**
	 * Permet de modifier une annonce de la BDD.
	 * 
	 * @param id_annonce L'ID de l'annonce à modifier.
	 * @param annonce La "nouvelle" annonce.
	 * 
	 * @return Le nombre de lignes modifiées dans la BDD, sinon -1 si l'annonce n'existe pas ou -2 si le vendeur associé n'existe plus.
	 */
	public int modifierAnnonce(int id_annonce, AnnonceDTO annonce)
	{
		Connection connection = null;
		PreparedStatement verification = null;
		PreparedStatement modification = null;
		
		int retour = 0;
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			// On vérifie que l'annonce existe bien
			verification = connection.prepareStatement("SELECT COUNT(*) AS existe FROM annonces WHERE id = ?");
			verification.setInt(1, id_annonce);
			ResultSet resultatVerification = verification.executeQuery();
			
			resultatVerification.next();
			
			// Si la quantité vaut 0, l'annonce n'existe pas et on retourne -1
			if(resultatVerification.getInt("existe") != 1)
			{
				resultatVerification.close();
				
				return -1;
			}
			
			resultatVerification.close();
			
			// On vérifie ensuite que le vendeur associé existe toujours
			VendeurDAO instance = VendeurDAO.getInstance();
			VendeurDTO v = instance.recupererVendeur(annonce.getId_vendeur());
			
			// Si son ID vaut -1, alors le vendeur n'existe pas et on retourne -2
			if(v.getId() < 0)
			{
				return -2;
			}
			
			// Si l'annonce existe et le vendeur associé également, on peut mettre à jour
			modification = connection.prepareStatement("UPDATE annonces SET date = ?, titre = ?, contenu = ?, departement = ?, emploi_stage = ?, objet_service = ? WHERE id = ?");
			modification.setDate(1, annonce.getDate());
			modification.setString(2, annonce.getTitre());
			modification.setString(3, annonce.getContenu());
			modification.setString(4, annonce.getDepartement());
			modification.setBoolean(5, annonce.isEmploi_stage());
			modification.setBoolean(6, annonce.isObjet_service());
			modification.setInt(7, id_annonce);
			
			retour = modification.executeUpdate();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{
			if(verification != null)
			{
				try
				{
					verification.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(modification != null)
			{
				try
				{
					modification.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}
		
		return retour;
	}
	
	
	
	/**
	 * Permet de supprimer une annonce de la BDD.
	 * 
	 * @param id_annonce L'ID de l'annonce à supprimer.
	 * 
	 * @return "true" si l'annonce a bien été supprimée, "false" sinon.
	 */
	public boolean supprimerAnnonce(int id_annonce)
	{
		Connection connection = null;
		PreparedStatement verification = null;
		PreparedStatement suppression = null;
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			// On vérifie que l'annonce existe bien
			verification = connection.prepareStatement("SELECT COUNT(*) AS existe FROM annonces WHERE id = ?");
			verification.setInt(1, id_annonce);
			ResultSet resultatVerification = verification.executeQuery();
			
			resultatVerification.next();
			
			// Si la quantité vaut 0, l'annonce n'existe pas et on retourne false
			if(resultatVerification.getInt("existe") != 1)
			{
				resultatVerification.close();
				
				return false;
			}
			
			resultatVerification.close();
			
			// Si l'annonce existe, on peut la supprimer
			suppression = connection.prepareStatement("DELETE FROM annonces WHERE id = ?");
			suppression.setInt(1, id_annonce);
			
			suppression.executeUpdate();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{
			if(verification != null)
			{
				try
				{
					verification.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(suppression != null)
			{
				try
				{
					suppression.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}

		return true;
	}
	
	
	
	/**
	 * Permet de récupérer une annonce depuis la BDD.
	 * 
	 * @param id_annonce L'ID de l'annonce à récupérer.
	 * 
	 * @return Une instance de {@link AnnonceDTO} contenant l'annonce si l'annonce est trouvée, une instance vide sinon.
	 */
	public AnnonceDTO recupererAnnonce(int id_annonce)
	{
		Connection connection = null;
		PreparedStatement verification = null;
		PreparedStatement recuperation = null;
		
		AnnonceDTO annonce = null;
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			// On vérifie que l'annonce existe bien
			verification = connection.prepareStatement("SELECT COUNT(*) AS existe FROM annonces WHERE id = ?");
			verification.setInt(1, id_annonce);
			ResultSet resultatVerification = verification.executeQuery();
			
			resultatVerification.next();
			
			// Si la quantité vaut 0, l'annonce n'existe pas et on retourne une instance vide
			if(resultatVerification.getInt("existe") != 1)
			{
				resultatVerification.close();
				
				return new AnnonceDTO();
			}
			
			resultatVerification.close();
			
			// Si l'annonce existe bien, on la récupère
			recuperation = connection.prepareStatement("SELECT * FROM annonces WHERE id = ?");
			recuperation.setInt(1, id_annonce);
			
			ResultSet resultatRecuperation = recuperation.executeQuery();
			
			resultatRecuperation.next();
			
			annonce = new AnnonceDTO(resultatRecuperation.getInt("id"), resultatRecuperation.getInt("id_vendeur"), resultatRecuperation.getDate("date"), resultatRecuperation.getString("titre"), resultatRecuperation.getString("contenu"), resultatRecuperation.getString("departement"), resultatRecuperation.getBoolean("emploi_stage"), resultatRecuperation.getBoolean("objet_service"));
			
			resultatRecuperation.close();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{
			if(verification != null)
			{
				try
				{
					verification.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(recuperation != null)
			{
				try
				{
					recuperation.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}

		return annonce;
	}
	
	
	
	/**
	 * Permet de récupérer sous formes résumées toutes les annonces de la BDD.
	 * 
	 * @return Une liste contenant des instances de {@link ResumeAnnonceDTO}.
	 */
	public List<ResumeAnnonceDTO> recupererToutesAnnonces()
	{
		Connection connection = null;
		PreparedStatement recuperation = null;
		
		List<ResumeAnnonceDTO> liste = new ArrayList<ResumeAnnonceDTO>();
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			// On récupère toutes les annonces
			recuperation = connection.prepareStatement("SELECT id, date, titre, departement, emploi_stage, objet_service FROM annonces");
			ResultSet resultatRecuperation = recuperation.executeQuery();
			
			// Tant qu'il en reste dans la BDD, on les ajoute à la liste
			while(resultatRecuperation.next())
			{
				liste.add(new ResumeAnnonceDTO(resultatRecuperation.getInt("id"), resultatRecuperation.getDate("date"), resultatRecuperation.getString("titre"), resultatRecuperation.getString("departement"), resultatRecuperation.getBoolean("emploi_stage"), resultatRecuperation.getBoolean("objet_service")));
			}
			
			resultatRecuperation.close();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{
			if(recuperation != null)
			{
				try
				{
					recuperation.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}
		
		return liste;
	}
	
	
	
	/**
	 * Permet de récupérer les annonces (sous formes résumées) associées à l'ID du vendeur passé en paramètre.
	 * 
	 * @param id_vendeur L'ID du vendeur.
	 * 
	 * @return Une liste contenant des instances de {@link ResumeAnnonceDTO} associées au vendeur. 
	 */
	public List<ResumeAnnonceDTO> recupererAnnoncesVendeur(int id_vendeur)
	{
		Connection connection = null;
		PreparedStatement recuperation = null;
		
		List<ResumeAnnonceDTO> liste = new ArrayList<ResumeAnnonceDTO>();
		
		try
		{
			connection = DriverManager.getConnection(adresseBDD, loginBDD, mot_de_passeBDD);
			
			if(disableAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			
			// On récupère tous les ID des annonces associées à l'ID du vendeur passé en paramètre
			recuperation = connection.prepareStatement("SELECT id, date, titre, departement, emploi_stage, objet_service FROM annonces WHERE id_vendeur = ?");
			recuperation.setInt(1, id_vendeur);
			
			ResultSet resultatRecuperation = recuperation.executeQuery();
			
			// Tant qu'il reste des annonces correspondantes dans la BDD, on les ajoute à la liste
			while(resultatRecuperation.next())
			{
				liste.add(new ResumeAnnonceDTO(resultatRecuperation.getInt("id"), resultatRecuperation.getDate("date"), resultatRecuperation.getString("titre"), resultatRecuperation.getString("departement"), resultatRecuperation.getBoolean("emploi_stage"), resultatRecuperation.getBoolean("objet_service")));
			}
			
			resultatRecuperation.close();
		}
		catch(SQLException sqlE)
		{
			sqlE.printStackTrace();
		}
		finally
		{
			if(recuperation != null)
			{
				try
				{
					recuperation.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
			
			if(connection != null)
			{
				try
				{
					connection.close();
				}
				catch(SQLException sqlE)
				{
					sqlE.printStackTrace();
				}
			}
		}
		
		return liste;
	}
	
	
	
	/**
	 * Permet de modifier la valeur de l'attribut disableAutoCommit.
	 * <p>
	 * <i>Cette méthode est utile uniquement à des fins de test.</i>
	 */
	public static void setDisableAutoCommit(boolean value)
	{
		if(value)
		{
			disableAutoCommit = true;
		}
		else
		{
			disableAutoCommit = false;
		}
	}
}
